package com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.dm;

import static com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.constant.CompatibilityConstant.DM_FUNCTION_GROUP_CONCAT;
import static com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.constant.CompatibilityConstant.DM_FUNCTION_GROUP_CONCAT_SEPARATOR;
import static com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.constant.CompatibilityConstant.DM_FUNCTION_JSON_SEARCH;
import static com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.constant.CompatibilityConstant.DM_FUNCTION_JSON_LENGTH;
import static com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.constant.CompatibilityConstant.DM_FUNCTION_PREFIX_$;
import static com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.constant.CompatibilityConstant.FUNCTION_JSON_LENGTH;
import static com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.constant.CompatibilityConstant.FUNCTION_JSON_SEARCH;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.shard.sql.mybatis.interceptor.compatibility.BaseFunctionCompatibility;
import com.ctrip.corp.obt.shard.utils.PluginUtils;

import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.MySQLGroupConcat;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;

/**
 * 函数兼容
 *
 * @author x.shen
 * @since 2023/11/26
 */
@Slf4j
public class DMFunctionCompatibility extends BaseFunctionCompatibility {
    /**
     * 通用函数存放，仅针对将不携带入参的函数替换 或 将入参去除的函数替换
     * 如：now(3) ===> now()
     * last_insert_id() ===> lastval()
     *
     */
    public static final Map<String, SubFunction> SQL_COMMON_FUNCTION_MAP = new ConcurrentHashMap<>(100);
    /**
     * 特殊函数处理，用于有自己特定表达式函数方式
     * 如：json_search ===> json_extract(column,'$.')
     */
    public static final Map<String, Consumer<Function>> SQL_SPECIAL_FUNCTION_MAP = new ConcurrentHashMap<>(100);

    public DMFunctionCompatibility() {
        SQL_COMMON_FUNCTION_MAP.put(FUNCTION_JSON_LENGTH, new SubFunction(DM_FUNCTION_JSON_LENGTH));

        // 特殊自定函数
        SQL_SPECIAL_FUNCTION_MAP.put(FUNCTION_JSON_SEARCH, DMFunctionCompatibility::handleJsonSearch);
    }

    /**
     * 特殊函数处理，由自己的兼容器处理
     *
     * @param expression
     */
    @Override
    public void handleSpecialFunction(Function expression) {
        if (CollectionUtils.isEmpty(SQL_SPECIAL_FUNCTION_MAP)) {
            return;
        }
        String expressionName = expression.getName().toLowerCase();
        if (!SQL_SPECIAL_FUNCTION_MAP.containsKey(expressionName)) {
            return;
        }
        // do something
        Consumer<Function> consumer = SQL_SPECIAL_FUNCTION_MAP.get(expressionName);
        if (Objects.nonNull(consumer)) {
            consumer.accept(expression);
        }
    }

    /**
     * dm兼容mysql的json函数
     *
     * @param expression
     */
    public static void handleJsonSearch(Function expression) {
        // 函数名称替换
        expression.setName(DM_FUNCTION_JSON_SEARCH);
        // 函数入参替换，添加前缀
        ExpressionList parameters = expression.getParameters();
        List<Expression> expressions = parameters.getExpressions();
        if (CollectionUtils.isNotEmpty(expressions)) {
            // index = 0 是 column
            Expression first = expressions.get(0);
            // index = len-1 是 value
            Expression last = expressions.get(expressions.size() - 1);
            if (first instanceof Column && !(last instanceof Column)) {
                List<Expression> newExpressions = new ArrayList<>();
                newExpressions.add(first);
                String lastValue = last.toString();
                // replaceAll "'" at the start and at the end ==> "
                lastValue = lastValue.replaceAll("'", "\"");
                newExpressions.add(new StringValue(DM_FUNCTION_PREFIX_$ + lastValue));
                parameters.setExpressions(newExpressions);
            }
        }
    }

    /**
     * dm兼容mysql的group_concat函数,group_concat不是一个function类型，而是单独的类型
     * 注意这里还会包含衍生函数 group_concat_separator
     * GROUP_CONCAT(DISTINCT p.passenger_name SEPARATOR ',') ==> REPLACE(WM_CONCAT(DISTINCT p.passenger_name) ,',',',')
     * GROUP_CONCAT(p.uid) ==> WM_CONCAT(p.uid)
     *
     * @param expression
     */
    public Expression handleGroupConcat(Expression expression) {
        if(!(expression instanceof MySQLGroupConcat)){
            return expression;
        }
        MySQLGroupConcat groupConcat = (MySQLGroupConcat) expression;

        // 重新构建一个function
        Function function = new Function();
        // 构造必要的参数
        function.setName(DM_FUNCTION_GROUP_CONCAT);
        function.setParameters(groupConcat.getExpressionList());
        // 设置distinct
        function.setDistinct(groupConcat.isDistinct());
        
        // 如果存在separator，那么需要重新定义规则
        if (StringUtils.isNotBlank(groupConcat.getSeparator())) {
            // 获取separator
            String separator = groupConcat.getSeparator();

            // 新建
            Function newFunction = new Function();
            newFunction.setName(DM_FUNCTION_GROUP_CONCAT_SEPARATOR);

            // 构建Expression
            List<Expression> expressions = new ArrayList<>();
            expressions.add(function);
            expressions.add(new StringValue("','"));
            expressions.add(new StringValue(separator));

            newFunction.setParameters(new ExpressionList(expressions));

            // 替换
            function = newFunction;
        }

        return function;
    }

    public void buildFunctionsSql(PluginUtils.DSBoundSql dsBoundSql) {
        // field函数处理
        handleFieldFunction(dsBoundSql);
    }

    /**
     * field函数兼容
     * 
     * @param dsBoundSql
     */
    private void handleFieldFunction(PluginUtils.DSBoundSql dsBoundSql) {
        String originalSql = dsBoundSql.sql();
        String pattern = "\\b(FIELD|field)\\s*\\((.*?)\\)";
        Matcher matcher = Pattern.compile(pattern).matcher(originalSql);
        StringBuffer compatibleSql = new StringBuffer();
        while (matcher.find()) {
            String match = matcher.group(2);
            String[] split = match.split(",");
            // split[0] --- columnName
            StringJoiner joiner = new StringJoiner(",");
            for (int i = 0; i < split.length; i++) {
                joiner.add(split[i]);
                if (i != 0) {
                    joiner.add(String.valueOf(i));
                }
            }
            // 添加默认值
            joiner.add("0");
            matcher.appendReplacement(compatibleSql, "decode" + "(" + joiner.toString() + ")");
        }
        matcher.appendTail(compatibleSql);
        dsBoundSql.sql(compatibleSql.toString());

    }
}
